#!/usr/bin/perl
use warnings;
use strict;
use Pod::Usage;
use Data::Dumper;
BEGIN {
    use File::Basename;
    my $path = dirname($0);
    $path = '.' unless defined $path && $path ne '';
    #print STDERR "pushing INC path: [$path]\n";
    push @INC, $path;
}

### user modules
use LogParse;
use DataName;
use CommandLine;
use SimpleError;
use DataAccess qw(:constants);

### options definition
my %options = (
    'input'          => +{ option => 'input|i=s'             , value =>    undef, check => 1 },
    'output'         => +{ option => 'output|o=s'            , value =>    undef, },
    #--
    'update'         => +{ option => 'update!'               , value =>        1, },
    'dumps'          => +{ option => 'dumps!'                , value =>        1, },
    'parse'          => +{ option => 'parse|p'               , value =>    undef, },
    'save'           => +{ option => 'save!'                 , value =>        1, },
    'help'           => +{ option => 'help|h|?'              , value =>    undef, },
    'manual'         => +{ option => 'manual'                , value =>    undef, },
    #--
    'config'         => +{ option => 'config|k=s'            , value =>    undef, },
    #--
    'data-list'      => +{ option => 'data-list|l=s'         , value =>    undef, },
    'addr-list'      => +{ option => 'addr-list|a=s'         , value =>    undef, },
    'log-struct'     => +{ option => 'log-struct|s=s'        , value =>    undef, },
    #--
    'dump-dies'      => +{ option => 'dump-dies|d=s'         , value =>    undef, },
    'dump-sql'       => +{ option => 'dump-sql|q=s'          , value =>    undef, },
    'sql-replace'    => +{ option => 'sql-replace'           , value =>    undef, },
    'sql-set'        => +{ option => 'sql-set'               , value =>    undef, },
    'sql-table'      => +{ option => 'sql-table|T=s'         , value =>    undef, },
    'dump-storable'  => +{ option => 'dump-storable|B=s'     , value =>    undef, },
    #--
    'read-struct'    => +{ option => 'read-struct|S=s'       , value =>    undef, },
    'struct-source'  => +{ option => 'struct-source|I=s'     , value =>    undef, },
    'struct-only'    => +{ option => 'struct-only'           , value =>    undef, },
    'dump-list'      => +{ option => 'dump-list|L=s'         , value =>    undef, },
    #--
    'guess'          => +{ option => 'guess|G!'              , value =>    undef, },
    'rename'         => +{ option => 'rename|N!'             , value =>    undef, },
    'retest'         => +{ option => 'retest|T=i'            , value =>    undef, },
    #--
    'FS'             => +{ option => 'field-separator|t=s'   , value =>      ',', },
);
use constant OPTION_DEFAULT => '.';
my %option_defaults = (
    'output'         => +{ reference => 'input', value => '*.dat'       , base => 1, },
    'log-struct'     => +{ reference => 'input', value => '*.lsout.csv' , base => 1, },
    'read-struct'    => +{ reference => 'input', value => '*.lsin.csv'  , base => 1, },
    'data-list'      => +{ reference => 'input', value => '*.dnout.txt' , base => 1, },
    'dump-list'      => +{ reference => 'input', value => '*.dnin.txt'  , base => 1, },
    'addr-list'      => +{ reference => 'input', value => '*.addr.csv'  , base => 1, },
    'dump-dies'      => +{ reference => 'input', value => '*.dies.csv'  , base => 1, },
    'dump-sql'       => +{ reference => 'input', value => '*.dies.sql'  , base => 1, },
    'dump-storable'  => +{ reference => 'input', value => '*.data.pl'   , base => 1, },
    'FS'             => +{ force => 1, value => sub {
                                                my $sep = shift; $sep =~ s/\.s/ /g;
                                                $sep =~ s/\.t/\t/g; return $sep; },  },
);

### command line interface
pod2usage(-exitstatus => 0, -verbose => 1) if @ARGV == 0;
CommandLine::get_options( \%options, 'config' );
pod2usage(-exitstatus => 0, -verbose => 1) if $options{'help'}{'value'};
pod2usage(-exitstatus => 0, -verbose => 2) if $options{'manual'}{'value'};
CommandLine::apply_checks( \%options );
CommandLine::apply_defaults( \%option_defaults,  \%options, OPTION_DEFAULT );
#message( "options with defaults applied:" );
#CommandLine::print_options( \%options );

### main

my ($da, $updates) = (undef, 0);
$da = do_input();
$updates = do_updates($da) if $options{'update'}{'value'};
do_dumps($da) if $options{'dumps'}{'value'};
if ($updates || $options{parse}{value}) {
    $da->store() if $options{'save'}{value};
} else {
    message("no updates/parsing executed, save skipped");
}

exit 0;

### subroutines

sub do_input {
    my $da = DataAccess::->new(Mode => DataAccess::DA_OVERWRITE); # always enable OVERWRITING!
    if ($options{parse}{value}) { # treat the input as .log_a text, and do parsing
        error("no output file specified") unless defined $options{output}{value};
        my $parser = LogParse::->new(
            Log_File => $options{input}{value}, LS_Source => $options{'struct-source'}{'value'});
        my $wafer  = $parser->parse();
        $da->set_data($wafer);
    } else {                      # input is already a Storable data
        $options{output}{value} = $options{input}{value} unless defined $options{output}{value};
        $da->retrieve($options{input}{value}); # this will set both data and file name
    }
    $da->set(FS => $options{FS}{value});
    $da->set_file($options{output}{value});
    return $da;
}

sub do_updates {
    my ($da)    = @_;
    my $updates = 0;
    my $dn      = DataName::->new(Data => $da->get_data());
    ### traversal
    if ($options{'guess'}{'value'}) {
        if (defined $options{'read-struct'}{'value'}) {
            warning("option 'read-struct' is present, guess is meaningless and skipped.");
        } else {
            $dn->guess_names();
            $updates++;
        }
    }
    if (my $struct_file = $options{'read-struct'}{'value'}) {
        my $renamed = $dn->read_names($struct_file);
        $updates++;
    }
    if ($options{'rename'}{'value'}) {
        $dn->rename_duplicates();
        $updates++;
    }
    if ( defined(my $reduce = $options{'retest'}{'value'}) ) {
        $da->reduce_retest($reduce);
        $updates++;
    }
    message("total $updates update(s) executed.");
    return $updates;
}

sub do_dumps {
    my ($da)  = @_;
    my $dumps = 0;
    my $dump_list = $options{'dump-list'}{value};
    ### traversal
    if ( defined(my $data_list = $options{'data-list'}{value}) ) {
        $da->dump_data_list($data_list);
        $dumps++;
    }
    if ( defined(my $addr_file = $options{'addr-list'}{value}) ) {
        $da->dump_addr_table($addr_file);
        $dumps++;
    }
    if ( defined(my $ls_file = $options{'log-struct'}{value}) ) {
        $da->dump_log_struct($ls_file);
        $dumps++;
    }
    if ( defined(my $dies_file = $options{'dump-dies'}{value}) ) {
        $da->dump_dies($dies_file, $dump_list);
        $dumps++;
    }
    if ( defined(my $sql_file = $options{'dump-sql'}{value}) ) {
        my $sql_mode = 0;
        $sql_mode |= DataAccess::DA_SQL_REPLACE if $options{'sql-replace'}{'value'};
        $sql_mode |= DataAccess::DA_SQL_SET     if $options{'sql-set'}{'value'};
        $da->set(SQL_Mode => $sql_mode);
        $da->set(SQL_Table => $options{'sql-table'}{value}) if defined $options{'sql-table'}{value};
        $da->dump_sql($sql_file, $dump_list);
        $dumps++;
    }
    message("total $dumps dump(s) executed");
    return $dumps;
}

__END__

=head1 NAME

=over 2

gendata - data generation from .log_a or update of an existing data file

=back

=head1 SYNOPSIS

=item * General

C<<< gendata -i INPUT -o OUTPUT [ Other Options... ] >>>

=item * Fresh parsing

C<<< gendata -i 3HEC44068101.log_a -p --output=. --log-struct=. >>>

=item * Update log struct

C<<< gendata -i 3HEC44068101.dies.dat --read-struct=input-ls.csv -l data-names.txt >>>

=item * CSV/SQL dumping

C<<< gendata -i 3HEC44068101.dies.dat -L data-names.txt -d. -q. >>>

=head1 Common Usage Scenarios

=over 2

=item * Fresh parsing

C<<< gendata -i 3HEC44068101.log_a -p --output=. --log-struct=. >>>

run the parsing, and dump the log struct for data name editing.
(a argument value of '.' means using the default one)

=item * Update log struct

C<<< gendata -i 3HEC44068101.dies.dat --read-struct=input-ls.csv -l data-names.txt >>>

retrieve the data file generated before, and update its log structure, specifically
the data names, and dump the names to a separate file.

For the data names dump list, you can comment/delete those not wanted, and then used
to dump dies' testing data.

=item * CSV/SQL dumping

C<<< gendata -i 3HEC44068101.dies.dat -L data-names.txt -d. -q. >>>

dump the dies data and SQL source, respectively, only data those listed in the data names
list file will be dumped.

For SQL dumping, please also see options `--sql-replace' and `--sql-set'.

=back

=head1 OPTIONS

=over 2

=item *

-i, --input=FILE

specify the input file to be processed.

this could be either a '.log_a' log file to be parsed (see `--parse'),
or a .dat file to be updated.

this option is mandatory

=item *

-o, --output=FILE

specify the output file name.

for an update operation (no --parse option specified), this could be omitted,
in which case the original input file will be used as output file (i.e., after
update, it will be overwritten, or do you wish to?).

for the --parse operation, this option is mandatory (after all, I will not overwrite
the original .log_a text file with a Perl Storable data).

=item *

-p, --parse

indicate that this is a parse operation, i.e., the --input value is a .log_a file.
otherwise, the $PROG will just treat the input as a Perl Storable data file.

=head1 DESCRIPTION

Just generates data!

=back

=cut
